classdef ProgressDisplayer < matlab.mixin.SetGet
    
    properties
        header char = '';
        total (1,1) double {mustBeReal, mustBeFinite, mustBeNonnegative} = 1;
        progress_scale (1,1) double {mustBeReal, mustBeFinite, mustBePositive} = 100;
        disable (1,1) logical = false;
        progress_step (1,1) double {mustBeReal, mustBeFinite, mustBeNonnegative} = 1;
        
        progress (1,1) double {mustBeReal, mustBeFinite, mustBeNonnegative} = 0;
        t_start (1,1) uint64 {mustBeReal, mustBeFinite, mustBeNonnegative} = 0;
    end
    
    methods
        function obj = ProgressDisplayer(header, total, progress_scale, disable)
            obj.header = header;
            obj.total = total;
            obj.progress_scale = progress_scale;
            obj.disable = disable;
        end
        
        function reset(obj)
            obj.progress = 0;
            obj.progress_step = floor(obj.total / obj.progress_scale);
        end
        
        function start(obj)
            obj.reset();
            obj.t_start = tic;
        end
        
        function step(obj, delta_progress)
            arguments
               obj (1,1) ProgressDisplayer
               delta_progress (1,1) double {mustBeInteger, mustBeNonnegative} = 1
            end
            progress_prev = obj.progress;
            obj.progress = obj.progress + delta_progress;

            if ~obj.disable && floor(obj.progress / obj.progress_step) > floor(progress_prev / obj.progress_step)
                t_elapsed = toc(obj.t_start);
                t_remain = t_elapsed / obj.progress * (obj.total - obj.progress);
                clc;
                
                disp([obj.header, ...
                    num2str(obj.progress  / obj.total * obj.progress_scale), '/', num2str(obj.progress_scale), ' completed', ...
                    newline,'time elapsed = ', num2str(t_elapsed), 'sec, time remain = ', num2str(t_remain), 'sec']);
            end
        end
    end
end